<?php

namespace App\Model;

/**
 * 获取包裹面单的基准类
 * User: wiggin
 */
abstract class PackageLabelAbstract
{
    protected static $_self = null;
    public $debug = false;

    private $getLabelEventPre = 'get_label_';
    private $setPackageEventPre = 'set_package_';

    protected $getLabelEvent = '';      //获取面单事件后缀名称 (如果继承后不设置 不开启事件记录)
    protected $setPackageEvent = '';    //设置包裹数据的事件后缀名称 (如果继承后不设置 不开启事件记录)
    protected $labelPath = '';          //设置面单的保存路径区分 (upload/pdflabel/$labelPath/date('Ymd'))

    /**
     * 公用参数 既是JAVA接口获取包裹的参数也是通过数据库查询需要获取面单的包裹参数
     */
    protected $warehouseIds = array();              //仓库ids
    protected $createTime = '2017-01-01 00:00:00';  //包裹的创建时间查询条件
    protected $packageId = '';                      //包裹id
    protected $pageSize = 500;                      //每页数量 （limit)
    protected $platformCode = array();              //平台code

    /**
     * JAVA接口设置的参数
     * @var array
     */
    protected $page = 1;                            //当前页

    /**
     * 获取需要面单的包裹列表参数
     * @var array
     */
    protected $packageList = array();               //待处理的包裹列表
    protected $condition = '';                      //获取包裹附加条件 (只考虑AND条件的,其他复杂条件重写方法)

    protected function __construct()
    {
        $this->warehouseIds = [41];
        if (empty($this->warehouseIds)) {
            throw new \Exception('仓库ID未配置,请先配置仓库ID');
        }
        //$this->importLabelApiFile();
    }

    /**
     * 单例模式
     * @return null
     */
    public static function getInstance()
    {
        if (is_null(static::$_self)) {
            static::$_self = new static();
        }
        return static::$_self;
    }

    /**
     * 设置参数
     * @param $config
     */
    public function setConfig($config)
    {
        if (is_array($config) && count($config)) {
            foreach ($config as $key => $val) {
                $this->{$key} = $val;
            }
        }
    }

    /**
     * 返回对应物流商的所有shipCodeArr
     * @return mixed
     */
    abstract public function getPackageShipCode();

    /**
     * 通过API方法获取面单
     * @param $data
     * @return array()  固定格式 array('status'=>true, 'data'=>pdfStr, 'errorMsg'=>'')
     */
    abstract public function getLabelByApi($data);

    /**
     * 导入对应的物流商渠道API文件
     * @return mixed
     */
    //abstract public function importLabelApiFile();

    /**
     * 初始化包裹列表通过api
     */
    public function setPackageList()
    {
        //获取本次需要请求的包裹数据条件
        $requestData = $this->setPackageListRequest();

        try {
            $this->eventStart($this->setPackageEvent, $this->setPackageEventPre);
            //通过API获取包裹列表
            $result = $this->getPackageListByApi($requestData);

            //Yii::log('获取包裹数据：result=>' . serialize($result), 'api_info');
            if ($result['status']) {
                //保存包裹数据
                $saveInfo = $this->savePackageList($result['data']);
            } else {
                throw new \Exception($result['errorMsg']);
            }
            $this->eventEnd($this->setPackageEvent, $this->setPackageEventPre, true, 'event end;getTotal :' . count($result['data']) . ';saveTotal:' . intval($saveInfo));
            echo '获取包裹数据成功;getTotal:', count($result['data']), 'saveTotal:', intval($saveInfo), '<br/>';
        } catch (\Exception $e) {
            $this->eventEnd($this->setPackageEvent, $this->setPackageEventPre, false, 'event end;Exception:' . $e->getMessage());
            echo '获取包裹数据异常：', $e->getMessage(), '<br/>';
        }
    }

    /**
     * 通过API获取包裹列表
     * @param $requestData
     * @return array
     */
    protected function getPackageListByApi($requestData)
    {
        return getPackageLabelApi::getInstance()->getPackageList($requestData);
    }

    /**
     * 保存包裹信息
     * @param $packageList
     * @return mixed
     */
    protected function savePackageList($packageList)
    {
        return OrderPackage::model()->savePackageInfo($packageList);
    }


    /**
     * @return array
     */
    public function setPackageListRequest()
    {
        $requestData = array(
            'shipCodeList' => $this->getPackageShipCode(),
            'warehouseIdList' => $this->warehouseIds,
            'createTime' => date('YmdHis', strtotime($this->createTime)),
            'pageNum' => $this->page,
            'pageSize' => $this->pageSize
        );
        if (!empty($this->packageId)) {
            $requestData = array(
                'packageId' => $this->packageId
            );
        }
        if (!empty($this->platformCode)) {
            $requestData['platformCode'] = $this->platformCode;
        }
        return $requestData;
    }

    /**
     * 获取需要面单的包裹列表
     */
    public function getNeedLabelPackageList()
    {
        $shipCode = $this->getPackageShipCode();
        $where = '';
        if (!empty($this->packageId)) {
            $row = OrderPackage::model()->getPackage($this->packageId, $shipCode);
            if ($row) {
                $this->packageList = array($row);
            }
        } else {
            $where = " create_time > '{$this->createTime}' ";
            if ($this->condition) {
                $where .= " AND {$this->condition} ";
            }
            $this->packageList = OrderPackage::model()->getNeedLabelPackageList($shipCode, $this->warehouseIds, $where, $this->platformCode, $this->pageSize);
        }
        if ($this->debug) {
            echo '获取包裹的参数：', join(',', $shipCode), ';', join(',', $this->warehouseIds), ';', $where, ';',
            join(',', $this->platformCode), ';', $this->pageSize, '<br/>';
        }
    }

    /**
     * 统一方法获取面单处理程序
     */
    public function getPackageLabel()
    {
        $result = array();
        try {
            //获取本次请求的包裹数据
            $this->getNeedLabelPackageList();
            if (empty($this->packageList)) {
                echo 'none package list';
                exit;
            }
            //开启事件
            $this->eventStart($this->getLabelEvent, $this->getLabelEventPre);
            $logModel = OrderPackageLabelLog::model();
            $logIds = $packageIds = array();
            $getCount = count($this->packageList);
            foreach ($this->packageList as $val) {
                $packageIds[] = $val['package_id'];
            }
            //预处理包裹的面单获取时间
            OrderPackage::model()->updatePackageLabelTime($packageIds);
            unset($packageIds);
            //遍历处理包裹获取面单
            foreach ($this->packageList as $val) {
                $logData = array(
                    'package_id' => $val['package_id'],
                    'ship_code' => $val['ship_code']
                );
                $logIds[$val['package_id']] = $logModel->addLog($logData);
                //调用对应的api接口获取包裹面单
                $labelRes = $this->getLabelByApi($val);
                //处理接口返回的包裹面单数据
                $this->doPackageLabelResponse($labelRes, $result, $val);
            }
            //记录包裹处理日志
            if ($result) {
                foreach ($result as $key => $val) {
                    if (!empty($logIds[$key])) {
                        $logData = array(
                            'id' => $logIds[$key],
                            'status' => $val['flag'] ? OrderPackageLabelLog::STATUS_OK : OrderPackageLabelLog::STATUS_FAIL,
                            'response_time' => date('Y-m-d H:i:s'),
                            'return_result' => $val['log']
                        );
                        $logModel->updateLog($logData);
                    }
                }
            } else {
                throw new \Exception('程序异常,接口处理数据的结果为空!');
            }
            $this->packageList = array();
            $this->eventEnd($this->getLabelEvent, $this->getLabelEventPre, true, 'event end;getTotal:' . $getCount . ';saveTotal:' . count($result));
            echo '获取面单结束;getTotal:', $getCount, 'saveTotal:', count($result), '<br>';
        } catch (\Exception $e) {
            $this->packageList = array();
            $this->eventEnd($this->getLabelEvent, $this->getLabelEventPre, false, 'Exception:' . $e->getMessage());
            echo '获取面单异常：', $e->getMessage(), '<br>';
        }
    }

    /**
     * 统一的处理包裹的面单方法 （只要返回的结果集与这个一样，就应该调用此方法)
     * @param $response
     * @param $return
     * @param $packageInfo
     * @throws CDbException
     */
    protected function doPackageLabelResponse($response, &$return, $packageInfo)
    {
        $packageId = $packageInfo['package_id'];
        if ($response['status']) {
            $saveInfo = $this->savePackagePdfLabel($response['data'], $packageId);
            if ($saveInfo['flag']) {
                $trans = Yii::app()->db->getCurrentTransaction();
                if (!$trans) {
                    $trans = Yii::app()->db->beginTransaction();
                }
                $packageData = array(
                    'package_id' => $packageId,
                    'print_label' => $saveInfo['filePath']
                );
                $packageLabelData = array(
                    'package_id' => $packageId,
                    'warehouse_id' => $packageInfo['warehouse_id'],
                    'platform_code' => $packageInfo['platform_code'],
                    'ship_code' => $packageInfo['ship_code'],
                    'print_label_path' => $saveInfo['filePath']
                );
                try {
                    $upFlag = OrderPackage::model()->savePackageLabelInfo($packageData);
                    $inFlag = OrderPackageLabelInfo::model()->savePackageLabelInfo($packageLabelData);
                    if ($upFlag && $inFlag) {
                        $trans->commit();
                        $return[$packageId] = array(
                            'flag' => true,
                            'log' => 'success'
                        );
                    } else {
                        $trans->rollback();
                        $return[$packageId] = array(
                            'flag' => false,
                            'log' => '包裹数据保存面单失败!'
                        );
                    }
                } catch (Exception $e) {
                    $trans->rollback();
                    $return[$packageId] = array(
                        'flag' => false,
                        'log' => '包裹数据保存面单失败!Exception:' . $e->getMessage()
                    );
                }
            } else {
                $return[$packageId] = array(
                    'flag' => false,
                    'log' => !empty($saveInfo['errorMsg']) ? $saveInfo['errorMsg'] : 'savePackagePdfLabel错误'
                );
            }
        } else {
            $return[$packageId] = array(
                'flag' => false,
                'log' => !empty($response['errorMsg']) ? $response['errorMsg'] : 'getPackageLabel错误'
            );
        }
    }

    /**
     * 根据pdf的字节流来保存为pdf的文件
     * @param $pdfStr
     * @param $packageId
     * @return array
     */
    public function savePackagePdfLabel($pdfStr, $packageId)
    {
        $returnArr = array(
            'flag' => false,
            'errorMsg' => ''
        );
        if (empty($pdfStr) || empty($packageId)) {
            $returnArr['errorMsg'] = '保存包裹面单的参数错误!';
            return $returnArr;
        }
        $checkInfo = MHelper::checkPdfAbnormal($pdfStr);
        if ($checkInfo['flag']) {
            $dirPath = $this->createLabelPath($this->labelPath);
            if (!$dirPath) {
                $returnArr['errorMsg'] = '面单保存的目录创建失败!';
                return $returnArr;
            }
            $filePath = $dirPath . '/' . $packageId . '.pdf';
            $res = file_put_contents($filePath, $pdfStr);
            if ($res != false) {
                $newStr = file_get_contents($filePath);
                $checkNewInfo = MHelper::checkPdfAbnormal($newStr);
                if ($checkNewInfo['flag']) {
                    if ($checkInfo['md5'] == $checkNewInfo['md5']) {
                        $returnArr['flag'] = true;
                        $returnArr['filePath'] = $filePath;
                        return $returnArr;
                    } else {
                        $returnArr['errorMsg'] = 'PDF文件已经损坏!';
                    }
                } else {
                    $returnArr['errorMsg'] = 'PDF文件已经损坏!';
                }
            } else {
                $returnArr['errorMsg'] = 'PDF保存文件失败!';
            }
        } else {
            $returnArr['errorMsg'] = 'PDF字节流已经损坏,PDF->' . $pdfStr;
        }
        return $returnArr;
    }

    /**
     * 创建面单存储路径
     * @param $labelPath
     * @return bool|string
     */
    protected function createLabelPath($labelPath)
    {
        $dirPath = !empty($labelPath) ? 'upload/pdflabel/' . trim($labelPath, '/') . '/' . date('Ymd') :
            'upload/pdflabel/' . date('Ymd');
        if (!file_exists($dirPath)) {
            $mkFlag = mkdir($dirPath, 0777, true);
            if (!$mkFlag) {
                return false;
            }
        }
        return $dirPath;
    }

    /**
     * 开启事件
     * @param $eventName
     * @return bool
     */
    public function eventStart($eventName, $prefix)
    {
        if (empty($eventName)) {
            return false;
        }
        $eventName = $prefix . $eventName;
        $flag = UEventControl::getInstance()->eventStart($eventName);
        if (!$flag) {
            echo '程序结束,当前事件正在执行中...<br/>';
            exit;
        }
    }

    /**
     * 结束事件
     * @param $eventName
     * @param $isSuccess
     * @param string $note
     * @return bool
     */
    public function eventEnd($eventName, $prefix, $isSuccess, $note = '')
    {
        if (empty($eventName)) {
            return false;
        }
        $eventName = $prefix . $eventName;
        UEventControl::getInstance()->eventEnd($eventName, $note, $isSuccess);
    }
}